/*
帧结构：
    字节0: 启动字符     (0x68)
    字节1: APDU长度    (后续ASDU的字节数)
    I.字节2: [发送序列号 N(S) 的低8位]
    I.字节3: [发送序列号 N(S) 的高7位 | 0]  (bit7=0)
    I.字节4: [接收序列号 N(R) 的低8位]
    I.字节5: [接收序列号 N(R) 的高7位 | 0]  (bit7=0)
    S.字节2: [0 0 0 0 0 0 0 0]
    S.字节3: [0 0 0 0 0 0 0 1]  (0x01)
    S.字节4: [接收序列号 N(R) 的低8位]
    S.字节5: [接收序列号 N(R) 的高7位 | 0]  (bit7=0)
    U.字节2: [控制功能位]
    U.字节3: [0 0 0 0 0 0 1 1]  (0x03)
    U.字节4: [0 0 0 0 0 0 0 0]
    U.字节5: [0 0 0 0 0 0 0 0]
    字节6:   类型标识    (1字节)
    字节7:   可变结构限定词 (1字节)(bit7:信息对象地址是否连续；bit6-0: 信息对象数量)
    字节8-9: 传送原因    (2字节)（低位在前)
    字节10-11: 公共地址   (2字节)（低位在前)
    字节12-...: 信息对象  (n字节)

品质：（紧跟在信息元素值后面）
    7  6  5  4  3  2  1  0
    |  |  |  |  |  |  |  |
    |  |  |  |  |  |  |  +-- OV (溢出 Overflow)
    |  |  |  |  |  |  +----- RES1 (保留)
    |  |  |  |  |  +-------- RES2 (保留)
    |  |  |  |  +----------- BL (阻塞 Blocked)
    |  |  |  +-------------- SB (取代 Substituted)
    |  |  +----------------- NT (非当前值 Not Topical)
    |  +-------------------- IV (无效 Invalid)
    +----------------------- RES3 (保留)
时标：在最后
    字节0: 毫秒低位     (&0xFF)
    字节1: 毫秒高位     (&0xFF)
    字节2: 分钟         (&0x3F)
    字节3: 小时         (&0x1F)bit8:夏令时标识
    字节4: 日           (&0x1F) 1...31
    字节5: 月           (&0x0F) 1...12
    字节6: 年           (&0x7F) 相对2000

约定：
    公共地址2字节
    信息对象3字节
    传输原因2字节
*/

#include "iec10X_frame.h"
#include "log.h"
#include <string.h>
#include <time.h>

static IEC10X_Frame_T STARTDT = {{0x68, 0x04, 0x07, 0x00, 0x00, 0x00}};
static IEC10X_Frame_T STARTDT_ACK = {{0x68, 0x04, 0x0B, 0x00, 0x00, 0x00}};
static IEC10X_Frame_T TESTFR = {{0x68, 0x04, 0x43, 0x00, 0x00, 0x00}};
static IEC10X_Frame_T TESTFR_ACK = {{0x68, 0x04, 0x83, 0x00, 0x00, 0x00}};
const IEC10X_Frame_T *IEC10X_FRAME_STARTDT()
{
    return &STARTDT;
}
const IEC10X_Frame_T *IEC10X_FRAME_STARTDT_ACK()
{
    return &STARTDT_ACK;
}
const IEC10X_Frame_T *IEC10X_FRAME_TESTFR()
{
    return &TESTFR;
}
const IEC10X_Frame_T *IEC10X_FRAME_TESTFR_ACK()
{
    return &TESTFR_ACK;
}

void IEC10X_Frame_Clear(IEC10X_Frame_T *self)
{
    memset(self->data, 0, sizeof(self->data));
}

//帧的长度
int IEC10X_Frame_Length(const IEC10X_Frame_T* self)
{
    if(self->data[0] != 0x68) return -1;
    return self->data[1];
}
//信息对象是否连续
int IEC10X_Frame_IsObjContinuous(const IEC10X_Frame_T* self)
{
    return self->data[7] >> 7;
}


//判等
int IEC10X_Frame_IsEqual(const IEC10X_Frame_T* frame1, const IEC10X_Frame_T* frame2)
{
    if(frame1 == frame2) return 1;
    if(IEC10X_Frame_Length(frame1) != IEC10X_Frame_Length(frame2)) return 0;
    return 0 == memcmp(frame1->data, frame2->data, IEC10X_Frame_Length(frame1));
}

//帧的总字节数
int IEC10X_Frame_Bytes(const IEC10X_Frame_T *self)
{
    return 2 + IEC10X_Frame_Length(self);
}

//帧的命令字
unsigned char IEC10X_Frame_Cmd(const IEC10X_Frame_T* self)
{
    return self->data[6];
}


//帧格式
IEC10X_Frame_FMT IEC10X_Frame_Fmt(const IEC10X_Frame_T* self)
{    
    // 判断I格式帧：控制域的第一个八位组的第一位比特为0
    if ((self->data[2] & 0x01) == 0) return IEC10X_Frame_FMT_I;  

    // 判断S格式帧：控制域的第一个八位组的第一位比特为1，第二位bit为0
    if ((self->data[2] & 0x03) == 0x1) return IEC10X_Frame_FMT_S;  
    
    // 判断U格式帧：控制域的笫一个八位位组的笫 1 位比特=1, 笫 2 位比特=1
    if ((self->data[2] & 0x03) == 0x3) {
        return IEC10X_Frame_FMT_U;  // U帧
    }
    

    return IEC10X_Frame_FMT_UNKNOWN;
}

//获取接收序号。低位在前。
unsigned short IEC10X_Frame_RecvID(const IEC10X_Frame_T* self)
{
    return (self->data[4]) | (self->data[5] << 8);
}
//获取发送序号。低位在前。
unsigned short IEC10X_Frame_SendID(const IEC10X_Frame_T* self)
{
    return (self->data[2]) | (self->data[3] << 8);
}
//获取公共地址
short IEC10X_Frame_Addr(const IEC10X_Frame_T* self)
{
    return (self->data[10]) | (self->data[11] << 8);
}
//获取信息对象数量
int IEC10X_Frame_ObjNum(const IEC10X_Frame_T* self)
{
    return self->data[7] & 0x7F;
}

//获取时标.返回自1970的毫秒数
long long IEC10X_Frame_Time(const IEC10X_Frame_T* self)
{
    long long ms;
    const unsigned char* buf = self->data + IEC10X_Frame_Bytes(self) - 7;
    struct tm t;
    t.tm_year = (buf[6] & 0x7F) + 2000 - 1900; //IEC104的年从2000开始，struct tm从1900开始
    t.tm_mon = (buf[5] & 0x0F) - 1; //IEC104的月从1开始，struct tm从0开始
    t.tm_mday = buf[4] & 0x1F; //IEC104的日从1开始，struct tm也从1开始
    t.tm_hour = buf[3] & 0x1F;
    t.tm_min = buf[2] & 0x3F;
    t.tm_sec = 0;
    t.tm_isdst = buf[3]>>7; //夏令时标识

    ms = mktime(&t); //此时是秒
    ms *= 1000;
    ms += (buf[0] | buf[1]<<8);
    return ms;
}

//设置帧命令字
void IEC10X_Frame_SetCmd(IEC10X_Frame_T* self, unsigned char cmd)
{
    self->data[6] = cmd;
}
    
// 设置传送原因（小端字节序：低位在前）
void IEC10X_Frame_SetReason(IEC10X_Frame_T* self, IEC10X_REASON reason)
{
    self->data[8] = (unsigned char)(reason & 0xFF);        // 低位字节
    self->data[9] = (unsigned char)((reason >> 8) & 0xFF); // 高位字节
}

//添加一个对象。
void IEC10X_Frame_AddObject(IEC10X_Frame_T* self, int obj_addr, unsigned char obj_val, IEC10X_QDS qds)
{
    int obj_num = IEC10X_Frame_ObjNum(self);
    unsigned char* obj = self->data + 12 + obj_num;
    *obj++ = (unsigned char)(obj_addr & 0xFF);  // 低位字节
    *obj++ = (unsigned char)((obj_addr >> 8) & 0xFF); 
    *obj++ = (unsigned char)(obj_addr >> 16 & 0xFF);  // 高位字节
    *obj++ =  obj_val;
    if(qds != IEC10X_QDS_NONE)
    {
        *obj = qds;
    }
    obj_num++;
    //修正对象个数和APDU长度
    self->data[7] = obj_num;
    if(obj_num == 1) self->data[1] = 14;
    else self->data[1] += 14;
    if(qds != IEC10X_QDS_NONE) self->data[1] += 1;
}

//设置序号。除U帧外，S帧和I帧都需要设置序号。可以在这里做通用代码，但要注意不要与其他SetXX冲突
void IEC10X_Frame_SetID(IEC10X_Frame_T* self, unsigned short recv_id, unsigned short send_id)
{
    // 设置发送序列号 (小端字节序)
    self->data[2] = (unsigned char)(send_id & 0xFF);        // 低位字节
    self->data[3] = (unsigned char)((send_id >> 8) & 0xFF); // 高位字节
    
    // 设置接收序列号 (小端字节序)
    self->data[4] = (unsigned char)(recv_id & 0xFF);        // 低位字节
    self->data[5] = (unsigned char)((recv_id >> 8) & 0xFF); // 高位字节

    //通用的
    self->data[0] = 0x68;
    if(self->data[1] == 0) self->data[1] = 6;
}

//设置公共地址
void IEC10X_Frame_SetAddr(IEC10X_Frame_T* self, short addr)
{
    self->data[10] = (unsigned char)(addr & 0xFF);        // 低位字节
    self->data[11] = (unsigned char)((addr >> 8) & 0xFF); // 高位字节
}